<?php

namespace App\Models\Flaw;

use App\Models\Access\Role\Role;
use App\Models\App;
use App\Models\Ogz;
use App\Services\Vuls\SendEmail;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Eloquent\SoftDeletes;
use App\Models\Flaw\Level;
use Illuminate\Validation\Rule;
use App\Models\Access\User\User;

class Vuls extends Model
{
    //使用软删除
    use SoftDeletes;
    //关联表
    protected  $table = 'vuls';
    protected   static $apps ;
    //要填充的字段
    protected $fillable = ['title','description','user_id','app_id','category_id','level_id','content','reward'
    ,'credit','status','is_delete','delete_at','vuls_number','form_url','url_parms','payload','check_id'
    ];
    //设置主键
    public $primaryKey = 'id';
    //软删除自动维护字段,不为空时此条数据不显示
    protected $dates = ['deleted_at'];
    /**
     * @var array  声明漏洞状态
     */
    public static $vulsStatus = [
        '0' => ['待审核'],
        '1' => ['已审核'],
        '2' => ['待确认'],
        '3' => ['待修复'],
        '4' => ['已修复'],
        '5' => ['已解决'],
        '6' => ['已关闭'],
        '7' => ['申诉中'],
        '8' => ['已忽略'],
        '9' => ['全部'],
        '10' => ['已拒绝'],
        '11' => ['无影响'],
    ];
    //状态常量
    const PENDING  = 0;//待审核
    const CLOSED   = 6;//已关闭
    const REVIEWED = 1;//已审核
    const IGNORE   = 8;//已忽略
    const NOEFFECT = 11;//无影响
    const SOLVED   = 5;//已解决
    const BECONFIRM= 2;//待确认
    const BEREPAIRE= 3;//待修复
    const CONFIRMED= 4;//已修复
    const APPEALING= 7;//申诉中

    //价值
    const SERIOUSVAL = 5000;
    const HIGHRISKVAL= 3000;
    const INRISKVAL  = 1000;
    const LOWVAL     = 200;
    /**
     * validate 表单数据验证
     *
     * @param $data
     * @return bool
     */
    public  static function validate($data){
        return  Validator::make($data, [
            'title'=>'required|max:50',
//            'user_id'=>'required',
            'app_id'=>'required|integer',
            'category_id' => [
                'required',
                'integer',
                Rule::exists('category','id')->where(function ($query) {
                    $query->where('pid', '!=', 0);
                }),
            ],
            'level_id'=>'required|integer',
/*            'from_url'=>'required',
            'url_parms'=>'required',
            'payload'=>'required|max:1000',*/
            'content'=>'required',
            'status'=>'required|integer',
            'description'=>'required',
        ],[
            'title.required' => '请填写漏洞标题！',
            'title.max' => '请填写50字以内的标题！',
//                'user_id.required' => '名称不能为空！',
            'app_id.required' => '请选择所属应用！',
            'app_id.integer' => '请选择所属应用！',
            'category_id.required' => '请选择所属栏目！',
            'category_id.integer' => '请选择所属栏目！',
            'category_id.exists' => '请选择二级分类！',
            'level_id.required' => '请选择所属级别！',
            'level_id.integer' => '请选择所属级别！',
/*            'from_url.required' => '请填写漏洞url来源！',
            'url_parms.required' => '请填写漏洞url参数！',
            'payload.required' => '请填写漏洞负载！',*/
            'content.required' => '请填写漏洞内容！',
            'status.required' => '请选择漏洞状态！',
            'status.integer' => '请选择漏洞状态！',
            'description.required' => '请填写漏洞相关描述信息！',
            ]
        );
    }

    /**
     * unionSelect 关联查询漏洞相关信息
     *
     * @param array $accessWhere 根据用户权限查询
     * @param array $where 基础查询条件
     * @param array $orWhere 与查询条件
     * @param array $whereBetween 日期查询
     * @param boolean $isSoftDel 是否查询软删除
     * @param array $scopeWhere 默认显示用户漏洞
     * @return mixed
     */
    public  static function  unionSelect($accessWhere = array(),$where = array(),$orWhere = array(),$whereBetween = array(),$isSoftDel = true,$scopeWhere = array()){
        $status = access()->hasStatus();
        $status = !empty($status) ? $status : [-1];
        $db = $isSoftDel ? Vuls::withoutTrashed() : Vuls::onlyTrashed();
        $exportLists = $lists = $db
            ->leftjoin('users', 'users.id', '=', 'vuls.user_id')
            ->leftjoin('level', 'level.id', '=', 'vuls.level_id')
            ->leftjoin('app', 'app.id', '=', 'vuls.app_id')
            ->leftjoin('category', 'category.id', '=', 'vuls.category_id')
            ->select('vuls.*', 'users.real_name as uname', 'users.name as account', 'level.name as lname', 'app.name as aname', 'category.name as cname', 'users.source', 'app.ogz_id','app.type')
            ->when($accessWhere, function ($query) use ($accessWhere) {
                return $query->whereIn('vuls.app_id',$accessWhere);
            })
              ->when($where, function ($query) use ($where) {
                  return $query->where($where);
              })
              ->when($status, function ($query) use ($status) {
                  return $query->whereIn('vuls.status',$status);
              })
            ->when($whereBetween, function ($query) use ($whereBetween) {
                return $query->whereBetween('vuls.created_at',[$whereBetween['start'],$whereBetween['end']]);
            })
            ->when($orWhere, function ($query) use ($orWhere) {
                return $query->Where(function($query)  use ($orWhere){
                    $query->where($orWhere['title'])->orWhere($orWhere['description']);
                });
            })
            //如果更新了应用联系人，也可以查看之前应用下的漏洞
            ->when($scopeWhere, function ($query) use ($scopeWhere) {
                return $query->orWhere(function($query)  use ($scopeWhere){
                    $query->where($scopeWhere);
                });
            })
            ->orderby('created_at', 'desc');
          return array(
              'export'=>$exportLists->get(),
              'paginate'=>$lists->paginate(10),
          );
    }

    /**
     * searchVarCol 返回搜索集合
     *
     * @param $accessInfo array 权限相关查询条件
     * @return \stdClass
     */
    public static function searchVarCol($accessInfo){
        $collection = new \stdClass;
        $categorys = new Category;
        $vuls = new Vuls;
        $collection->ulists = User::getUserWithRoles([Role::ROLE_SUPER_ADMIN,Role::ROLE_ADMIN,Role::ROLE_AUDIT_ADMIN]);
        $collection->categorys = $categorys->getCategoryInfo();
        $collection->levels = Level::withoutTrashed()->select('id','name')->get();
        $ogzChildIds = $accessInfo['ogzs']['child'];
        $collection->ogzs = $vuls->getOgzInfo()->each(function($item) use ($ogzChildIds){
            return $item->hasaccess = in_array($item->id,$ogzChildIds) ? 'yes': 'non';
        });
        $collection->apps = DB::table('app')->select('id','ogz_id','name')->whereIn('id',$accessInfo['apps'])->get();
        return $collection;
    }

    /**
     * getStatusRecords 后台首页根据漏洞状态统计
     *
     * @return mixed
     */
    public static function  getStatusRecords($appIds){
        return DB::table('vuls')
            ->select(DB::raw('count(*) as count,min(status) as type'))
            ->whereIn('app_id',$appIds)
            ->groupBy('status')
            ->get();
    }
    /**
     * getStatusRecords 后台首页根据漏洞等级统计
     *
     * @return mixed
     */
    public static function getLevelRecords($appIds){
        return DB::table('level')
            ->leftjoin('vuls', 'level.id', '=', 'vuls.level_id')
            ->select(DB::raw('count(level.id) as count,min(level.id) as lid,max(level.name) as lname'))
            ->whereIn('app_id',$appIds)
            ->groupBy('level.id')
            ->get();
    }

    /**
     * getOgz 递归查找
     * @param $sourceItems array 原数组
     * @param $targetItems array  重组后的数组
     * @param int $pid 父id
     * @param string $str 展现形式
     * @param int $i 控制空格数量
     */
    public function getOgz($sourceItems, $targetItems, $pid=0, $str='',$i=0){
        $str .= $i>=1 ? "&nbsp;&nbsp;├&nbsp;&nbsp;":'';
        foreach ($sourceItems as $k => $v) {
            if($v->pid == $pid){
                $v->num = substr_count($str,"├")+1;
                $targetItems[] = $v;
                $i++;
                $this->getOgz($sourceItems, $targetItems, $v->id, $str,$i);
            }
        }
    }

    /**
     * getOgzInfo 获取组织机构
     *
     * @param boolean $softDel 是否查询软删除数据
     * @return Collection
     */
    public function getOgzInfo($softDel = true){
        $collection = $softDel ? Ogz::withoutTrashed() : Ogz::onlyTrashed();
        $sourceItems = $collection->get();
        //调整多级下拉菜单样式
        $pids = DB::table('ogz')->pluck('pid')->unique();
        $sourceItems->each(function($item) use( $pids){
            $item->child = $pids->search($item->id)?'yes':'non';
        });
        if(!$softDel) return $sourceItems;
        $targetItems = new Collection;
        $this->getOgz($sourceItems, $targetItems);
        return $targetItems;
    }
    /**
     * 状态统计数量
     * @param array $accessWhere
     * @return array
     */
    public static function statusNumber ($accessWhere){
        $number = [0,1=>0,2=>0,3=>0,4=>0];
        $list =self::leftjoin('users', 'users.id', '=', 'vuls.user_id')
            ->leftjoin('level', 'level.id', '=', 'vuls.level_id')
            ->leftjoin('app', 'app.id', '=', 'vuls.app_id')
            ->leftjoin('category', 'category.id', '=', 'vuls.category_id')
            ->whereIn('vuls.app_id',$accessWhere)
            ->select('vuls.status')->get();
        foreach ($list as $k => $item) {
            switch ($item->status){
                case self::PENDING:{
                    $number[0]++;
                    break;
                }
                case self::BECONFIRM:{
                    $number[1]++;
                    break;
                }
                case self::BEREPAIRE:{
                    $number[2]++;
                    break;
                }
                case self::CONFIRMED:{
                    $number[3]++;
                    break;
                }
                case self::APPEALING:{
                    $number[4]++;
                    break;
                }
            }
        }
        return $number;
    }
    /**
     * 待评分数量
     * @param array $accessWhere
     * @return int
     */
    public static function BecommitNumer ($accessWhere){
        return self::leftjoin('users', 'users.id', '=', 'vuls.user_id')
            ->leftjoin('level', 'level.id', '=', 'vuls.level_id')
            ->leftjoin('app', 'app.id', '=', 'vuls.app_id')
            ->leftjoin('category', 'category.id', '=', 'vuls.category_id')
            ->whereIn('vuls.app_id',$accessWhere)
            ->where([['vuls.reward','>',0],['vuls.is_reasonable','<>',2],['vuls.status','>=',Vuls::BEREPAIRE]])->count();
    }
    /**
     * 漏洞价值
     * @param int $level_id
     * @return int
     */
    public static function getValue($level_id){
        $val = 0;
        switch ($level_id){
            case Level::SERIOUSID;
                $val = self::SERIOUSVAL;
                break;
            case Level::HIGHRISKID;
                $val = self::HIGHRISKVAL;
                break;
            case Level::INRISKID;
                $val = self::INRISKVAL;
                break;
            case Level::LOWID;
                $val = self::LOWVAL;
                break;
        }
        return $val;
    }

    /**
     * 未修复及时邮件
     * @param array $vuls
     */
    public static function unconfirmEmail ($vul){

        $service = new SendEmail();
        $appInfo = App::select(['name as app_name','user_id'])->where(['app.id'=>$vul->app_id])->first();
        $emailData = [
            'recive' => '海航集团信息安全应急响应中心',
            'connect' => '',
            'APP_ROOT' => env('APP_URL') . '/' . env('APP_ROOT') . '/',
            'currentTime' => date('Y年m月d日'),
            'time' => getStrTime(),
            'from' => $appInfo->app_name
        ];
        $userName = User::getNameString(explode(',',$appInfo->user_id));
        $userEmail = User::getEmailString(explode(',',$appInfo->user_id));

        $service->setInfo($emailData);
        //内容中的发件人信息
        $service->sendUsers($userEmail,$userName);
        $service->receiver($userEmail, $userName);
        $service->subject = $appInfo->app_name;
        $vul->level_name = Level::where(['id'=>$vul->level_id])->value('name');
        $service->unconfirm($vul);
    }

    /**
     * 驳回申诉及时邮件
     */
    public static function rejectEmail ($vul){

        $service = new SendEmail();
        $appInfo = App::select(['name as app_name','user_id'])->where(['app.id'=>$vul->app_id])->first();
        $emailData = [
            'recive' => '海航集团信息安全应急响应中心',
            'connect' => '',
            'APP_ROOT' => env('APP_URL') . '/' . env('APP_ROOT') . '/',
            'currentTime' => date('Y年m月d日'),
            'time' => getStrTime(),
            'from' => $appInfo->app_name
        ];
        $userName = User::getNameString(explode(',',$appInfo->user_id));
        $userEmail = User::getEmailString(explode(',',$appInfo->user_id));

        $service->setInfo($emailData);
        //内容中的发件人信息
        $service->sendUsers($userEmail,$userName);
        $service->receiver($userEmail, $userName);
        $service->subject = $appInfo->app_name;
        $vul->level_name = Level::where(['id'=>$vul->level_id])->value('name');
        $service->reject($vul);
    }
    /**
     * 属性拓展
     * @param $data
     */
    public static function extendAttr ($data){
        foreach ($data as $item){
            //增加价值
            $item->value = self::getValue($item->level_id);
            //审核延期开关
            $item->audit_delay = self::canAuditDelay($item->id);
            //申请延期
            $item->can_delay   = self::canDelay($item->id);
        }
        return $data;
    }

    /**
     * 是否可以审核延期
     * @param int $vuls_id
     * @return bool
     */
    public static function canAuditDelay ($vuls_id){
        $canAuditDelay = false;
        $agree = VulsDelay::where(['vuls_id'=>$vuls_id])->value('agree');
        if (!is_null($agree) && $agree==0){
            $canAuditDelay = true;
        }
        return $canAuditDelay;
    }
    /**
     * 是否可以申请延期
     * @param int $vuls_id
     * @return bool
     */
    public static function canDelay ($vuls_id){
        $canDelay = true;
        $agree = VulsDelay::where(['vuls_id'=>$vuls_id])->value('agree');
        if ($agree && $agree==1){
            $canDelay = false;
        }
        return $canDelay;
    }

    /**
     * 获取指定时间范围app数量
     * @param array $appIds
     * @param array $betweenTime
     * @return int
     */
    public static function getCountBetweenTime ($appIds,$betweenTime){
        $c = 0;
        if (is_array($appIds)){
            $c =self::select('id')->whereBetween('created_at',$betweenTime)->whereIn('app_id',$appIds)->groupBy('app_id')->get();
            $c = $c->count();
        }
        return $c;
    }

    /**
     * 延期时间长度
     * eg:严重延期为7天修复，默认3天内（级别配置处）修复，延长了4天
     * @param int $id
     * @return int
     */
    public static function getDelayedDay(int $id){
        $delayed = 0;
        $vul = self::select('vuls.check_at','vuls.delayed_at','l.term')->leftJoin('level as l','vuls.level_id','=','l.id')->where(['vuls.id'=>$id])->first();
        $auditTime = DB::table('vuls_record')
            ->where([['vuls_id','=',$id],['vuls_status','=',self::BECONFIRM]])
            ->value('created_at');
        //邮件时间为准，否则审核时间
        $auditTime = !is_null($vul->check_at) ? Carbon::parse($vul->check_at ): Carbon::parse($auditTime);

        $delayedDay = !is_null($vul->delayed_at) ? Carbon::parse($auditTime)->diffInDays(Carbon::parse($vul->delayed_at)) : 0;
        if ($delayedDay-$vul->term > 0 )
            $delayed = $delayedDay-$vul->term;
        return $delayed;
    }

    /**
     * 驳回操作重新分配
     * @param array $vuls
     * @param array $vulsRecord
     * @return bool
     */
    public static function reallocation ($vuls,$vulsRecord){

        return DB::transaction(function () use ($vuls,$vulsRecord){
            //更新内容
            if (self::where('id', $vuls['id'])
                ->update($vuls)
                &&
            //添加记录
            VulsRecord::insert($vulsRecord)){
                return true;
            } else {
                return false;
            }
        });
    }
}